//      stl-helper.h
//      
//      Standard Template (STL) helpers 
//         macros, file access, log number operations, type conversions
//         random permutation
//      
//      Copyright 2012 tqlong <tqlong@espada>
//      
//      This program is free software; you can redistribute it and/or modify
//      it under the terms of the GNU General Public License as published by
//      the Free Software Foundation; either version 2 of the License, or
//      (at your option) any later version.
//      
//      This program is distributed in the hope that it will be useful,
//      but WITHOUT ANY WARRANTY; without even the implied warranty of
//      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//      GNU General Public License for more details.
//      
//      You should have received a copy of the GNU General Public License
//      along with this program; if not, write to the Free Software
//      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
//      MA 02110-1301, USA.

#ifndef STL_HELPER_H_
#define STL_HELPER_H_
#include <armadillo>
#include <vector>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <numeric>
#include <functional>
#include <limits>
#include <cmath>

#include <boost/math/special_functions/log1p.hpp>
#include <boost/foreach.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/program_options.hpp>
#include <boost/date_time/posix_time/posix_time.hpp> 

//#include "opencv2/highgui/highgui.hpp"

#define ENDL std::endl
#define pb push_back

#define sz(c) ((int)((c).size()))
//(RAND::get_size(c))
//#define nele(c) ((int)(c).n_elem)
#define tr(c, it) for (typeof((c).begin()) it = (c).begin(); it != (c).end(); it++)
#define all(c) (c).begin(), (c).end()
#define ct(c, k) ((c).find(k) != (c).end())

#define sqr(x) ((x)*(x))
#define DOUBLE_INFINITY std::numeric_limits<double>::infinity()

#define FATAL_ERROR(x) { std::cerr << "line " << __LINE__ << " file " << __FILE__ << " " << x << ENDL; exit(1); }
#define MSG(x) { std::cerr << x << ENDL; }
#define MSG_VERBOSE(vm, x) { if (vm.count("verbose")) std::cerr << x << ENDL; }

#define FileAccessWrite(s, f) std::ofstream s; FileAccess::open2write(s, f);
#define FileAccessRead(s, f) std::ifstream s; FileAccess::open2read(s, f);
#define FileAccessAppend(s, f) std::ofstream s; FileAccess::open2append(s, f);
#define FileAccessLine(s, l); std::string l; std::getline(s, l);
#define FileAccessLineSplit(s, l, t, d) std::string l; std::getline(s, l); std::vector<std::string> t; boost::split(t, l, boost::is_any_of(d), boost::token_compress_on);
#define FileAccessAppendRow(s, v) FileAccess::append_row(s, v);

#define VM(vm, param, type) (vm[param].as<type>())

typedef std::vector<int> vi_t;
typedef std::vector<vi_t> vvi_t;
typedef arma::vec vd_t;
typedef std::vector<vd_t> vvd_t;

class RAND 
{
 public:
  static vi_t randperm(int n);

  template <typename T>
    static T s2type(const std::string& s) 
    {
      T tmp;
      std::stringstream ss(s);
      ss >> tmp;
      return tmp;
    }

  template <typename InputIterator>
    static double get_entropy( InputIterator first, InputIterator last )
    {
      double sum = 0;
      for (InputIterator i = first; i != last; i++) 
	sum += *i;

      double entropy = 0;
      for (InputIterator i = first; i != last; i++) {
	double p = *i / sum;
	entropy += (p < 1e-10) ? 0 : -p * ::log(p);
      }
      return entropy;
    }

  static int random_idx(const vd_t& p) {
    double s = arma::sum(p);
    double r = arma::randu() * s - p(0);
    int i = 0;
    while (i < sz(p) && r > 0) {
      i ++;
      r -= p(i);
    }
    if (i >= sz(p)) FATAL_ERROR("Failed to generate random index");
    return i;
  }
	
  static double KLdivergence(const vd_t& p, const vd_t& q) 
  {
    double sum = 0;
    for (int i = 0; i < sz(p) && i < sz(q); i++) {
      sum += p(i) * ( log(p(i)) - log(q(i)) );
    }
    return sum;
  }
  
  static int get_size(const vd_t& x) { return x.n_elem; }

  template <typename T>
    static int get_size(const std::vector<T>& x) { return (int)x.size(); }

  static int get_size(const std::string& x) { return x.size(); }
};

class FileAccess
{
 public:
  static void open2read(std::ifstream& input, const std::string& fName);
  static void open2write(std::ofstream& output, const std::string& fName);
  static void open2append(std::ofstream& output, const std::string& fName);  
  static std::string getline(std::ifstream& input);
  static void readrows(std::ifstream& input, vvd_t& s);
  static void append_row(std::ostream& output, const arma::vec& v);
};

class LogNumber
{
 public:
  static double add(double a, double b);
  static double div(double a, double b);
  static double mul(double a, double b);
  static double zero();
  static double one();
  static double randu(double s = 0);
  static double sum(const arma::vec& v);  
  static void normalize(arma::vec& v);
};

template <typename Object, typename BinaryOp = std::less<double> >
  struct Accumulate
  {
    double& current_value;
    Object& obj;
    BinaryOp op;
  
    Accumulate (double& v, Object& o, BinaryOp op1 = std::less<double>()) 
    : current_value(v), obj(o), op(op1)
    {
    }
  
    Accumulate& operator() (const Accumulate& another) {
      if ( op(current_value, another.current_value) ) {
	current_value = another.current_value;
	obj = Object(another.obj);
      }
      return *this;
    }
  
    Accumulate& operator() (const double& v, const Object& o) {
      if ( op(current_value, v) ) {
	current_value = v;
	obj = Object(o);
      }
      return *this;
    }
  };
/*
class IMG {
 public:
  static arma::vec gray2vec(const cv::Mat& m) 
  {
    arma::vec x(m.rows * m.cols);
    int k = 0;
    for (int i = 0; i < m.rows; i++) 
      for (int j = 0; j < m.cols; j++)
	x[k++] = (double) m.at<uchar>(i, j) / 255; // [0,1] scale
    return x;
  }

  static arma::vec vec_threshold(const arma::vec& x, double thres, bool POS_dir = true) 
  {
    arma::vec v = x;
    for (int i = 0; i < sz(x); i++) {
      v(i) = ((POS_dir && x(i) > thres) || (!POS_dir && x(i) <= thres)) ? 1 : -1;
    }
    return v;
  }

  static void vec2bw(const arma::vec& x, cv::Mat& m, cv::Size size) {
    m.create(size, CV_32F);
    int k = 0;
    for (int i = 0; i < m.rows; i++) 
      for (int j = 0; j < m.cols; j++)
	m.at<float>(i, j) = (x(k++) == 1 ? 1 : 0);
  }
};
*/
#endif
